﻿using System;
using System.IO;
using System.Xml;
using System.Data;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;

using iTunesPwned.Core.Plugin;
using iTunesPwned.Core.iTunes;

using FileWatcher.Action;
using FileWatcher.Controls;

namespace FileWatcher
{
    public class FileWatcher : PluginBase, ISettings, IPlayer
    {
        public event UpdateSettingsDelegate UpdateSettings;

        private XmlDocument m_doc;
        private XmlElement m_root;
        private FileWatcherControl m_tabItem;
        private Dictionary<Guid, WatchDog> m_registeredWatchDogs;

        public FileWatcher() 
            : base(new Guid("3ADF0C7F-853B-4c30-B48C-EE12573318D7"), "File Watcher")
        {
            this.m_registeredWatchDogs = new Dictionary<Guid, WatchDog>();

            this.m_tabItem = new FileWatcherControl();

            this.m_tabItem.Tag = true;

            this.m_doc = new XmlDocument();
        }

        private void tabItem_UpdateWatchDogStateEvent(Guid identifier, FileWatcherSettings settings)
        {
            XmlDocumentFragment frag;
            XmlNode node = this.FindNode(identifier);

            switch (settings.State)
            {
                case FileWatcherSettings.FileWatcherState.Running:
                    this.m_registeredWatchDogs[identifier].Start();
                    break;
                case FileWatcherSettings.FileWatcherState.Stopped:
                    this.m_registeredWatchDogs[identifier].Stop();
                    break;
            }

            frag = this.m_doc.CreateDocumentFragment();
            frag.InnerXml = settings.Serialize();

            this.m_root.ReplaceChild(frag, node);

            this.UpdateSettings(this);
        }

        private void tabItem_SaveSettingsEvent(Guid identifier, FileWatcherSettings settings)
        {
            XmlDocumentFragment frag;
            XmlNode node = this.FindNode(identifier);

            this.m_registeredWatchDogs[identifier].UpdateSettings(settings);

            frag = this.m_doc.CreateDocumentFragment();
            frag.InnerXml = settings.Serialize();

            this.m_root.ReplaceChild(frag, node);

            this.UpdateSettings(this);
        }

        private void tabItem_GetSettingsEvent(Guid identifier)
        {
            if (!this.m_registeredWatchDogs.ContainsKey(identifier)) return;

            this.m_tabItem.UpdateSettings(this.m_registeredWatchDogs[identifier].Settings);
        }

        private void tabItem_FolderChangeEvent(Guid identifier, string folder)
        {
            XmlDocumentFragment frag;
            FileWatcherSettings settings;
            XmlNode node = this.FindNode(identifier);

            if (node == null)
            {
                settings = new FileWatcherSettings(folder, identifier);
                CopyAction action = new CopyAction(settings.Delete);
                settings.CopyFolder = action.Destination;

                frag = this.m_doc.CreateDocumentFragment();
                frag.InnerXml = settings.Serialize();

                this.m_root.AppendChild(frag);

                this.m_tabItem.UpdateSettings(settings);

                this.m_registeredWatchDogs.Add(identifier, new WatchDog(settings, action));
            }
            else
            {
                settings = this.m_registeredWatchDogs[identifier].Settings;

                if (settings.State == FileWatcherSettings.FileWatcherState.Running)
                {
                    this.m_registeredWatchDogs[identifier].Stop();
                }

                this.m_root.RemoveChild(node);

                this.m_registeredWatchDogs.Remove(identifier);
            }

            this.UpdateSettings(this);
        }

        private XmlNode FindNode(Guid identifier)
        {
            foreach (XmlNode node in this.m_root.ChildNodes)
            {
                if (node.Attributes["identifier"].Value == identifier.ToString())
                {
                    return node;
                }
            }

            return null;
        }

        private void SettingsTab(object sender, RoutedEventArgs e)
        {
            this.RegisterTabItem(this.m_tabItem);
        }

        public override void Init()
        {
            this.m_tabItem.FolderChangeEvent += new FolderChangeDelegate(tabItem_FolderChangeEvent);

            this.m_tabItem.GetSettingsEvent += new GetSettingsDelegate(tabItem_GetSettingsEvent);

            this.m_tabItem.SaveSettingsEvent += new SaveSettingsDelegate(this.tabItem_SaveSettingsEvent);

            this.m_tabItem.UpdateWatchDogStateEvent += new UpdateWatchDogStateDelegate(tabItem_UpdateWatchDogStateEvent);

            this.m_tabItem.Selected = 0;

            this.RegisterMenuItem("Settings", new RoutedEventHandler(this.SettingsTab));

            this.RegisterTabItem(this.m_tabItem);
        }

        #region ISettings Members

        public string Serialize()
        {
            return this.m_doc.OuterXml;
        }

        public void Deserialize(string settings)
        {
            if (settings == null || settings == string.Empty)
            {
                this.m_root = this.m_doc.CreateElement("Settings");

                this.m_doc.AppendChild(this.m_root);

                return;
            }

            this.m_doc.Load(new StringReader(settings));

            this.m_root = this.m_doc.DocumentElement;

            FileWatcherSettings fileSettings;

            foreach (XmlNode child in this.m_root.ChildNodes)
            {
                fileSettings = new FileWatcherSettings();
                fileSettings.Deserialize(child.OuterXml);

                this.m_tabItem.AddFolder(fileSettings.Identifier, fileSettings.Root);

                this.m_registeredWatchDogs.Add(fileSettings.Identifier, new WatchDog(fileSettings));

                switch (fileSettings.Action)
                {
                    case "Copy":
                        this.m_registeredWatchDogs[fileSettings.Identifier].Action = new CopyAction(fileSettings.Delete, fileSettings.CopyFolder);
                        break;
                    case "iTunes":
                        this.m_registeredWatchDogs[fileSettings.Identifier].Action = new iTunesAction(this.Player, fileSettings.Delete);
                        break;
                }

                if (fileSettings.State == FileWatcherSettings.FileWatcherState.Running)
                {
                    this.m_registeredWatchDogs[fileSettings.Identifier].Start();
                }
            }
        }

        #endregion

        #region IPlayer Members

        public iTunesPlayer Player
        {
            get;
            set;
        }

        #endregion
    }

    public class FileWatcherSettings
    {
        public enum FileWatcherState { Running, Stopped };

        public bool Recursive { get; set; }
        public bool ProcessExisting { get; set; }
        public bool Delete { get; set; }

        public int Timeout { get; set; }
        public int Cycle { get; set; }

        public string AcceptPattern { get; set; }
        public string Root { get; private set; }
        public string Action { get; set; }
        public string CopyFolder { get; set; }

        public FileWatcherState State { get; set; }

        public Guid Identifier { get; set; }

        public FileWatcherSettings()
        {
            this.CopyFolder = "";
            this.Action = "Copy";
            this.Recursive = true;
            this.ProcessExisting = false;
            this.Delete = false;
            this.Timeout = 5;
            this.Cycle = 3;
            this.AcceptPattern = "*.*";
            this.Root = "";
            this.State = FileWatcherState.Stopped;
            this.Identifier = Guid.Empty;
        }

        public FileWatcherSettings(string folder)
            : this()
        {
            this.Root = folder;
        }

        public FileWatcherSettings(string folder, Guid identifier)
            : this(folder)
        {
            this.Identifier = identifier;
        }

        public string Serialize()
        {
            XmlDocument doc = new XmlDocument();
            XmlElement root = doc.CreateElement("WatchDog");
            root.SetAttribute("identifier", this.Identifier.ToString());
            XmlElement element;

            element = doc.CreateElement("accept");
            element.InnerText = this.AcceptPattern;
            root.AppendChild(element);

            element = doc.CreateElement("action");
            element.InnerText = this.Action;
            root.AppendChild(element);

            element = doc.CreateElement("copyfolder");
            element.InnerText = this.CopyFolder;
            root.AppendChild(element);

            element = doc.CreateElement("cycle");
            element.InnerText = this.Cycle.ToString();
            root.AppendChild(element); 
            
            element = doc.CreateElement("delete");
            element.InnerText = this.Delete.ToString();
            root.AppendChild(element); 
            
            element = doc.CreateElement("identifier");
            element.InnerText = this.Identifier.ToString();
            root.AppendChild(element); 
            
            element = doc.CreateElement("processexist");
            element.InnerText = this.ProcessExisting.ToString();
            root.AppendChild(element); 
            
            element = doc.CreateElement("recursive");
            element.InnerText = this.Recursive.ToString();
            root.AppendChild(element);

            element = doc.CreateElement("root");
            element.InnerText = this.Root;
            root.AppendChild(element);

            element = doc.CreateElement("state");
            element.InnerText = Enum.GetName(typeof(FileWatcherState), this.State);
            root.AppendChild(element);

            element = doc.CreateElement("timeout");
            element.InnerText = this.Timeout.ToString();
            root.AppendChild(element);

            doc.AppendChild(root);

            return doc.InnerXml;
        }

        public void Deserialize(string xml)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(new StringReader(xml));
            XmlElement root = doc.DocumentElement;

            this.CopyFolder = root["copyfolder"].InnerText;
            this.Action = root["action"].InnerText;
            this.AcceptPattern = root["accept"].InnerText;
            this.Cycle = int.Parse(root["cycle"].InnerText);
            this.Delete = bool.Parse(root["delete"].InnerText);
            this.Identifier = new Guid(root["identifier"].InnerText);
            this.ProcessExisting = bool.Parse(root["processexist"].InnerText);
            this.Recursive = bool.Parse(root["recursive"].InnerText);
            this.Root = root["root"].InnerText;
            this.State = (FileWatcherState)Enum.Parse(typeof(FileWatcherState), root["state"].InnerText);
            this.Timeout = int.Parse(root["timeout"].InnerText);
        }
    }
}